# -*- coding: UTF-8 -*-
# __author__ = 'Sengo'
from settings import C, P, SCREEN_SIZE
from OpenGL.GL import *
from cal_service import transformation
from random import randint

angle = 0


def do_z_buffer():
    # 初始化z_buffer
    global angle
    print angle
    glClear(GL_COLOR_BUFFER_BIT)
    depth_buffer, frame_buffer = init_z_buffer()
    scan_line(depth_buffer, frame_buffer, angle)
    glBegin(GL_POINTS)
    for x in range(len(frame_buffer)):
        for y in range(len(frame_buffer)):
            color = frame_buffer[x][y]
            glColor3f(color[0], color[1], color[2])
            glVertex2i(x, y)
    glEnd()
    glFlush()

    angle += 10
    angle %= 360


def scan_line(depth_buffer, frame_buffer, angle=None):
    # init data
    d = 3.8
    f = 1
    h = 0.5
    V_prime = [0, 0.5, 0]  # Y-direction of camera
    vertexes, lines = transformation(C, P, V_prime, d, f, h, angle)

    # 初始化边表
    surfaces = []
    for line in lines:
        surface = []
        for v in line:
            vertex = vertexes[v][:-1]
            surface.append(vertex)
        surfaces.append(surface)

    # 对每个面
    for srfc in surfaces:
        r = randint(1, 255)/255.0
        g = randint(1, 255)/255.0
        b = randint(1, 255)/255.0
        # glColor3f(r, g, b)
        color = (r, g, b)
        # glBegin(GL_POINTS)
        smax, smin = find_scan_line_scope(srfc)
        # 创建一个面的活化表 和 AET表
        edge_table = create_edge_table(smax, smin)
        AET = edge_model()
        # 取出当前点1前后相邻的共4个点，点1与点2的连线作为本次循环处理的边，点1与点2的连线作为本次循环处理的边，
        # 另外两个点点0和点3用于计算奇点
        v_num = len(srfc)
        for i in range(v_num):
            x0 = srfc[(i-1+v_num) % v_num][0]
            x1 = srfc[i][0]
            z1 = srfc[i][2]
            x2 = srfc[(i+1) % v_num][0]
            z2 = srfc[(i+1) % v_num][2]
            x3 = srfc[(i+2) % v_num][0]
            y0 = srfc[(i-1 + v_num) % v_num][1]
            y1 = srfc[i][1]
            y2 = srfc[(i + 1) % v_num][1]
            y3 = srfc[(i + 2) % v_num][1]
            # 水平线直接舍弃
            if y1 == y2:
                continue

            # 计算下端点y坐标、上端点y坐标、下端点x坐标和斜率倒数
            ymin = y1 if y1 < y2 else y2
            ymax = y1 if y1 > y2 else y2
            xmin = x1 if y1 < y2 else x2
            dx = (x1-x2) * 1.0 / (y1-y2)

            # 奇点特殊处理，若点2->1->0的y坐标单调递减则y1为奇点，若点1->2->3的y坐标单调递减则y2为奇点
            if (y2 > y1 > y0) or (y1 > y2 > y3):
                ymin += 1
                xmin += dx

            # 创建一条边【ymax, xmin, dx, vertex1, vertex2】
            edge = edge_model()
            edge.ymax = ymax
            edge.xmin = xmin
            edge.dx = dx
            edge.edge_next = edge_table[ymin]
            edge.edge_vertex1 = [x1, y1, z1]
            edge.edge_vertex2 = [x2, y2, z2]
            edge_table[ymin] = edge

        # 扫描线从下往上扫描，y坐标每次加1
        do_scan(smin, smax, edge_table, AET, color, depth_buffer, frame_buffer)
        # glEnd()
        # glFlush()


def do_scan(ymin, ymax, edge_table, AET, color, depth_buffer, frame_buffer):
    # 扫描线从下往上扫描，y坐标每次加1
    for y in range(ymin, ymax + 1):
        # 取出ET中当前扫描行的所有边并按x的递增顺序（若x相等则按dx的递增顺序）插入AET
        while edge_table[y]:
            edge_insert = edge_table[y]  # 拿出edge_table的边来插入活化边

            edge_in_AET = AET
            # 在AET中搜索合适的插入位置 按x（若x相等则按∆x）递增方向排序。
            while edge_in_AET.edge_next:
                if edge_insert.xmin > edge_in_AET.edge_next.xmin:
                    edge_in_AET = edge_in_AET.edge_next
                    continue

                if edge_insert.xmin == edge_in_AET.edge_next.xmin and edge_insert.dx > edge_in_AET.edge_next.dx:
                    edge_in_AET = edge_in_AET.edge_next
                    continue
                # 找到位置
                break

            # 将pInsert插入AET的当前位置
            edge_table[y] = edge_insert.edge_next  # 指向下一个边
            edge_insert.edge_next = edge_in_AET.edge_next
            edge_in_AET.edge_next = edge_insert

        # AET中的边两两配对并填色
        p = AET
        while p.edge_next and p.edge_next.edge_next:
            # 左边的边的两个端点
            v1 = p.edge_next.edge_vertex1
            v2 = p.edge_next.edge_vertex2
            # 右边的边的两个端点
            v3 = p.edge_next.edge_next.edge_vertex1
            v4 = p.edge_next.edge_next.edge_vertex2
            # 左边的边的中间的点，见PPT 15页
            Za = v1[2] - (v1[2]-v2[2]) * (v1[1]-y)*1.0 / (v1[1] - v2[1])
            # 右边的边的中间的点
            Zb = v3[2] - (v3[2]-v4[2]) * (v3[1]-y)*1.0 / (v3[1] - v4[1])
            for x in range(int(p.edge_next.xmin), int(p.edge_next.edge_next.xmin)):
                # TODO X, Y, Z, COLOR
                xa = int(p.edge_next.xmin)
                xb = int(p.edge_next.edge_next.xmin)
                xp = x
                Zp = Zb - (Zb - Za) * (xb - xp)*1.0 / (xb - xa)

                if x >= SCREEN_SIZE or y >= SCREEN_SIZE or x <= 0 or y <= 0:
                    continue

                if Zp < depth_buffer[x][y]:
                    depth_buffer[x][y] = Zp
                    frame_buffer[x][y] = color
                # glVertex2i(x, y)

            p = p.edge_next.edge_next
        # 删除AET中满足y=ymax的边
        p = AET
        while p.edge_next:
            if p.edge_next.ymax == y:
                p.edge_next = p.edge_next.edge_next
            else:
                p = p.edge_next

        # 更新AET中边的x值，进入下一循环
        p = AET
        while p.edge_next:
            p.edge_next.xmin += p.edge_next.dx
            p = p.edge_next


def find_scan_line_scope(vertexes):
    # 在所有点中找出的Ymax和Ymin， 就是扫描线的扫描范围
    ymax = 0
    ymin = SCREEN_SIZE
    for v in vertexes:
        if v[1] > ymax:
            ymax = v[1]
        if v[1] < ymin:
            ymin = v[1]
    return ymax, ymin


def create_edge_table(ymax, ymin):
    edge_table = {}
    for i in range(ymin, ymax+1):
        edge_table[i] = None
    return edge_table


class edge_model:
    def __init__(self):
        self.ymax = None
        self.xmin = None
        self.dx = None
        self.edge_next = None
        self.edge_vertex1 = None
        self.edge_vertex2 = None


def init_z_buffer():
    # init depth_buffer
    depth_buffer = [[1 for col in range(SCREEN_SIZE)] for row in range(SCREEN_SIZE)]

    frame_buffer = [[(0, 0, 0) for col in range(SCREEN_SIZE)] for row in range(SCREEN_SIZE)]

    return depth_buffer, frame_buffer


if __name__ == '__main__':
    e1 = edge_model()
    e2 = edge_model()
    e3 = edge_model()
    e_in = edge_model()
    e1.dx = 1
    e2.dx = 2
    e3.dx = 3
    e_in.dx = 2.5

    e1.edge_next = e2
    e2.edge_next = e3
    e3.edge_next = None

    p = e1
    while p.edge_next:
        if e_in.dx > p.edge_next.dx:
            p = p.edge_next
            continue
        break

    e_in.edge_next = p.edge_next
    p.edge_next = e_in

    print e1.dx
    print e1.edge_next.dx
    print e1.edge_next.edge_next.dx
    print "====="